ShowTable of Contents
What is the IBM Sametime Proxy Server?
Introduced with IBM® Sametime® version 8.5, the Sametime Proxy Server is a Web-based application server allowing applications to securely integrate with IBM Sametime and benefit from its real-time collaborative features, using a simple HTTP-based Representational State Transfer (REST) API and the JavaScript Object Notation (JSON) data format.
The main integration targets are rich Internet applications written in Java
TMScript, but thanks to its powerful and language-agnostic REST API, the Sametime Proxy Server can be used to provide real-time collaborative capabilities to any applications.
In this article, we specifically demonstrate how it can be used to integrate IBM Sametime with Java desktop applications. To get the most from the article, you should have a general understanding of HTTP and the REST architecture, and a feature-level knowledge of IBM Sametime.
Sametime Proxy Server is part of the Sametime Standard offering and runs on top of IBM WebSphere® Application Server. Like all other components of the Sametime infrastructure, it can (but doesn't need to) be administered using the Sametime System Console. To run the Sametime Proxy server, you must have a pre-installed Sametime Community Server (see figure 1).
Figure 1. Sametime Proxy and Community Servers
Integrating with the Sametime Proxy Server
The Sametime Proxy Server offers a rich, powerful, and secure REST API, which is documented in the Sametime Software Developer Kit (SDK). Like all REST APIs, it is used by making an HTTP request to the appropriate URI on the server and passing the relevant parameters.
The sample code attached to this article shows how this can be done. One specific technical point, however, deserves a little more explanation before we go into the nitty-gritty details of the code.
The asynchronous update problem
HTTP is essentially a disconnected, request – response protocol, in which a client (like a browser) makes a request for a resource and receives it to present to the user. IBM Sametime, on the user hand, is a real-time collaborative system in which random updates can arrive at any time (think of a user in your contact list changing his status from “Available” to “In a meeting”).
Because the client doesn't know when an update will be available, it cannot make a request to retrieve it, and the server doesn't have any other way to send it to the client.
Comet and the long poll
To work around this issue, the Sametime Proxy Server uses a technique known as “Comet”, in which a separate, long-held HTTP request is made to the server by the client to receive an asynchronous update. We refer to this separate request as the “long poll”.
The long poll is initiated by the client (see figure 2). Until an asynchronous update arrives, the server keeps the long-poll connection open (hence its name). When an asynchronous notification arrives, the server uses the opened long-poll connection to send it to the client. The client receives the notification and re-starts a new long-poll connection.
If no asynchronous updates have arrived after a certain time (about 30 seconds), the long poll is terminated by the server with no response, and the client must restart it.
Figure 2. Long poll in action
The Proxy Server uses JSON as a data format, and the long poll is no exception; the updates received by the long poll are JSON data packets. For reference,
JSON is a lightweight, text-based, data interchange format.
Remember, the long poll is an additional connection and is not used by the client to send requests to the server, only to receive asynchronous updates. The client can create additional “standard” HTTP requests to the server to retrieve resources and make REST APIs calls.
Setting up the development environment
Now that we understand the concepts, we can get started with the code writing. For the purpose of integrating desktop Java applications with the Sametime Proxy Server, we need the following:
Follow these steps to import the code provided with this article into the Eclipse development environment:
attached .zip file and unzip them on the workstation.
2. Start Eclipse and select Window --- Preferences from the menu, to open the Preferences window.
3. Navigate to the Java --- Build Path --- User Libraries Preference window and click the New button, to create a new user library.
4. Type “JSON-SIMPLE” for the library name, and click OK
5. Now Click the Add JARs button, and select the json-simple.1.1.1.jar file unzipped in Step 1 from the json-simple package.
6. Repeat steps 4 and 5 to create another user library named “APACHE_HTTPCOMPONENTS” and containing the following JARs from the Apache HttpComponents package unzipped in Step 1:
-
commons-codec-1.4.jar
-
commons-logging.1.1.1.jar
-
httpclient-4.1.3.jar
-
httpcore-4.1.4.jar
7. The Preferences window should now look like that in figure 3. Click OK to close it.
Figure 3. User Libraries Preferences window

8. Select File --- Import, to open the Import window.
9. In the tree view, select General --- Existing Projects into Workspace, and click Next.
10. Select the Select archive file field and browse for the .zip file containing the sample code for this article downloaded in Step 2; click Finish.
You now have the sample project STProxyClient opened in your Eclipse development environment.
Managing the Proxy Server long-poll connection
In this section we show how a Java application can log into the Sametime Proxy Server and handle the long-poll connection to receive asynchronous updates.
All the REST APIs used in this section are documented in the “Browser Client Toolkit Guide” of the Sametime SDK.
Logging in and establishing the long poll
The first thing the application must do is log in to the Sametime Proxy Server, using the Login REST API, Its URI is stwebapi/user/connect, its method POST, and it accepts the parameters listed in table 1.
Table 1. Login REST API parameters
|
|
|
|
|
|
|
|
|
|
|
The type of login (by password, by token, etc ...)
|
|
|
The initial presence status (available, away, etc ...)
|
|
|
The initial presence message
|
To log in, we create an HTTP POST request, set the parameters, and execute it (see listing 1). The response contains a JSON object and, if the request is successful, the object contains a value named sessionid, which is the unique ID assigned by the Proxy Server to this user's session. We will need it to establish the long poll, so we extract it from the JSON response.
Listing 1. The login() method of the SametimeProxyClient class
public JSONObject login(String userName, String password, Integer initialStatus, String initalMessage) throws IOException, SametimeProxyClientException {
// The list of parameters for the REST API call
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(LOGIN_LOGIN_METHOD_PARAM, LoginMethod.BY_PASSWORD.getMethod()));
if (userName == null)
throw new NullPointerException();
params.add(new BasicNameValuePair(LOGIN_USERNAME_PARAM, userName)); // The user name
if (password == null)
throw new NullPointerException();
params.add(new BasicNameValuePair(LOGIN_PASSWORD_PARAM, password)); // The password
if (initialStatus != null)
params.add(new BasicNameValuePair(LOGIN_INITIALSTATUS_PARAM, String.valueOf(initialStatus)));
if (initalMessage != null)
params.add(new BasicNameValuePair(LOGIN_INITIALMESSAGE_PARAM, initalMessage));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
// This REST call requires a HTTP POST
HttpPost method = new HttpPost(LOGIN_REQUEST_URI); // URI is /user/connect
method.setEntity(entity);
// Making the HTTP request and retrieving the Session ID from the response
JSONObject json = executeProxyRestMethodAndRelease(method);
String sessionId = (String) json.get(USER_CONNECT_SESSION_ID_JSON_FIELD);
// Starting the long poll handler
m_longPollHandler.start(this, sessionId, userName);
return json;
}
Next, our application must establish (and manage) the long poll. This is achieved in our sample code by the ProxyLongPollHandler class.
Once the application has successfully logged in and retrieved the user session ID, it starts the long poll by making a HTTP PUT request to the /stwebapi/RTCServlet/
/user (where is replaced by the session ID obtained from the log-in) with a “userName” parameter containing the logged-in user name (see listing 2).
If the request is successful, it returns a JSON object containing a unique value known as the rtc4web nonce. This will be used in subsequent long-poll requests to link them to this specific user session. To that end, we save it in the m_nonce field.
Listing 2. Initiating the long poll
public void start(ProxyLongPollListener listener, String sessionIdentifier, String userName) throws IOException, SametimeProxyClientException {
int status = 0;
HttpEntity entity = null;
Reader jsonResponse = null;
HttpResponse response = null;
if (m_sessionId != null)
throw new SametimeProxyClientException("Long poll handler already started");
m_listener = listener;
// Creating the long poll initiation HTTP request
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair(USER_NAME_PARAMETER, userName));
try {
String uriString = MessageFormat.format(LONG_POLL_INIT_REQUEST_URI, new Object[] { sessionIdentifier });
URI uri = URIUtils.createURI(null, null, -1, uriString, URLEncodedUtils.format(params, "UTF-8"), null);
HttpPut method = new HttpPut(uri);
// Executing the long poll initiation HTTP request
response = execute(method);
if (response.getStatusLine().getStatusCode() == 200) {
entity = response.getEntity();
jsonResponse = new InputStreamReader(entity.getContent(), "UTF-8");
JSONParser parser = new JSONParser();
JSONObject obj = (JSONObject) parser.parse(jsonResponse);
if (obj.get(ERROR_JSON_FIELD) == null) {
// Request is successful - we save the nonce value and initiate the long-poll handling thread
m_nonce = (String) obj.get(RTC4WEB_NONCE_JSON_FIELD);
m_sessionId = sessionIdentifier;
Thread longPollThread = new Thread(this);
longPollThread.start();
Once this request has been made and the nonce retrieved, we can start emitting the long-poll requests themselves (see listing 3). Because they are made continuously, and independent from the other requests to the Proxy Server, a separate thread is started whose role is to run the long-poll requests, retrieve their content (if any), and continuously restart them when they terminate.
The nonce must be set as an HTTP header named “rtc4web-nonce” in all long-poll requests. When content arrives, an object implementing a listener interface (the ProxyLongPollListener interface, in our code) will be notified and passed to the JSON object.
Listing 3. Running the long poll
// Creating the long poll HTTP request and executing it
HttpGet pollingMethod = new HttpGet(LONG_POLL_REQUEST_URI);
pollingMethod.setHeader(RTC4WEB_NONCE_HEADER, m_nonce);
try {
response = execute(pollingMethod);
entity = response.getEntity();
jsonResponse = new InputStreamReader(entity.getContent(), "UTF-8");
JSONParser parser = new JSONParser();
JSONObject obj = (JSONObject) parser.parse(jsonResponse);
// Notifying the long poll listener
m_listener.incomingData(obj);
Terminating the long poll and logging out
When your application needs to disconnect from the Proxy Server, it must close the long poll before logging out. Closing the long poll is a two-step process in which a request is first made to suspend the long poll and then a second one to terminate it.
As shown in listing 4, to suspend the long poll, an HTTP PUT request is made to the /stwebapi/RTCServlet//endUpdate URI (where is replaced by the session ID obtained from the log-in). This causes the currently running long-poll request to return (without any data).
Because we have notified the server that we want to suspend the long poll, the long-poll connection should not be re-established. In our code sample, we simply terminate the thread that was repeatedly making the long-poll connections.
Listing 4. Suspending the long poll
// Creating and executing the long poll suspension HTTP request
HttpPut method = new HttpPut(MessageFormat.format(LONG_POLL_STOP_REQUEST_URI, new Object[] { m_sessionId }));
method.setHeader(RTC4WEB_NONCE_HEADER, m_nonce);
try {
synchronized (this) {
m_isStarted = false;
}
response = execute(method);
Once the long poll is suspended, it must be properly terminated so that the server can release resources associated with this user session. As shown in listing 5, we do this by making an HTTP DELETE request to the /stwebapi/RTCServlet/<session-id>/user URL (where <session-id> is replaced by the session ID obtained from the log-in).
Listing 5. Terminating the long poll
// Creating and executing the long poll termination request
HttpDelete method = new HttpDelete(MessageFormat.format(LONG_POLL_TERMINATE_REQUEST_URI, new Object[] { m_sessionId }));
method.setHeader(RTC4WEB_NONCE_HEADER, m_nonce);
try {
response = execute(method);
Finally, once the long poll is terminated, the application can log out by using the Logout REST API, whose URI is “stwebapi/user/connect”, method is DELETE, and which doesn't take any parameter (see listing 6).
Listing 6. Logging out
public JSONObject logout() throws IOException, SametimeProxyClientException {
m_longPollHandler .stop();
m_longPollHandler .terminate();
HttpDelete method = new HttpDelete(LOGIN_REQUEST_URI);
return executeProxyRestMethodAndRelease(method);
}
Our application can now log in to the Proxy Server, receive asynchronous updates from the server, and then log out.
Making changes and receiving updates
In this section we show how a Java application logged into the Sametime Proxy Server can make changes (such as changing the logged-in-user presence status or status message) and react to the asynchronous updates it receives via the long-poll connection (such as the other users' presence status updates).
Changing the user presence status & status message
We change the user presence status and message, like everything else in the Proxy Server, by invoking the appropriate REST API. In this case, the API URI is "/stwebapi/user/status", the HTTP method is POST, and the accepted parameters are listed in table 2.
Table 2. SetStatus REST API parameters
|
|
|
|
|
Optional.
The presence status. 1=Available, 2=Away, etc ... – see the SDK documentation for more possible values.
|
|
|
Optional.
The status message as a String
|
Code listing 7 shows how to invoke this REST API to change the status, the status message, or both. The important point to remember is that the “rtc4web-nonce” HTTP header must be set, and that its value must be the nonce value received when initiating the long poll. In our sample code, a dedicated method is provided that sets the header and executes the HTTP request.
Listing 7. Setting the user presence status and status message
public JSONObject setStatus(UserStatus status, String statusMessage) throws IOException, SametimeProxyClientException {
// Filling the parameter list
List<NameValuePair> params = new ArrayList<NameValuePair>();
if (status != null)
params.add(new BasicNameValuePair(SETSTATUS_STATUS_PARAM, status.toString()));
if (statusMessage != null)
params.add(new BasicNameValuePair(SETSTATUS_STATUSMESSAGE_PARAM, statusMessage));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
// Creating the HTTP POST request
HttpPost method = new HttpPost(SETSTATUS_REQUEST_URI);
method.setEntity(entity);
// Executing the request - the executeProxyRestMethodAndRelease() method will set the rtc4web-nonce header
return executeProxyRestMethodAndRelease(method);
}
Using the sample application provided with this article, you can execute this code by following these steps:
1. Start the application with the following command line parameters in this order: the Proxy Server host name, the Proxy Server port number, the user login, and the user password.
2. Wait for the application to start, log in, and establish the long poll.
3. When the prompt appears, type setStatus 5 “I am in a meeting”.
4. The logged-in-user presence status should be set to “In a Meeting”, and its status message should be “I am in a meeting”.
5. When you're done, type “quit” to terminate the application.
Receiving status update from other users
Now that we have shown how to change the logged-in-user status, we demonstrate how to receive other users' presence status update.
This is a two step process, wherein the user whose updates are to be received must first be added to the “watch list”, allowing the client to register its “interest” in receiving updates for this user. To add the user to the watch list, a HTTP POST request must be made to the “/stwebapi/presence” URI, with the parameters listed in table 3.
Table 3. UpdateWatchList REST API parameters
|
|
|
|
|
|
|
|
Optional.
A list of users separated by ';' to add to the watch list
|
|
|
Optional.
A public group id to add to the watch list.
|
|
|
Optional.
Should the whole buddy list be added to the watch list
|
This REST API invocation is implemented in the updateWatchList() method of the SametimeProxyClient class. You can invoke it from the provided sample application prompt to add a user to the watch list by typing updateWatchList add <user> null false (replacing <user> with a user id such as an email address).
When the user has been successfully added to the watch list, the server sends an asynchronous update to notify the application (see listing 8).
Listing 8. JSON notification for addition to watch list
{
key:
sid: 6da5f78c-60d2-430c-b0ad-0c485d1d86fb
time: 1330557454663
cn: 6da5f78c-60d2-430c-b0ad-0c485d1d86fb
type: channel
value: {
returnCode: 200
displayName: Freddie Mercury
userId: CN=Freddie Mercury,CN=Users,DC=stoidub10,DC=com
action: [
watchlist
addUser
]
resolvedName: fmercury@stoidub10.com
}
op: add
}
All asynchronous updates follow the same format: An update array of objects, each of them containing a value object, which in turn contains a two-string array under the action key and other data. The action array indicates the type of updates that has been received, and the other data is information relevant to this update. Table 4 lists a few possible updates and their associated action array values.
Table 4. Some action array values and their meaning
|
|
|
|
|
User successfully added to watch list
|
|
|
Watchlist user update (status, message, attribute, etc ...)
|
|
|
A chat request was received
|
|
|
The chat partner started / stopped typing
|
|
|
A chat message has arrived
|
|
|
The chat partner closed the chat window
|
|
|
A multi-user chat invitation was received
|
In this case, the action array contains the value “watchlist” and “addUser,” indicating a user was added to the watchlist. The resolvedName value contains the id that was passed to the update watch list REST API (allowing us to link that invocation to this asynchronous update), and the userId value contains the Sametime ID of the user who was added. This value is useful as further asynchronous updates will use it to identify the user to whom they apply.
When the user who was added to the watch list presence status changes, another update will be sent (see listing 9). This update action array contains the values “watchlist” and “watchlist,” indicating a watchlist user update. The accompanying “watchlist” object contains the user Sametime ID, his new status (5 for “in a meeting”) and his status message. By parsing this data packet, the application is notified of the status update of the user and can react accordingly.
Listing 9. JSON notification for user presence status update
{
key:
sid: 5759f61b-d992-4747-a540-e84b46adad54
time: 1330558853020
cn: 5759f61b-d992-4747-a540-e84b46adad54
type: channel
value: {
watchlist: [
{
id: CN=Freddie Mercury,CN=Users,DC=stoidub10,DC=com
statusMessage: I am in a meeting
status: 5
}
]
action: [
watchlist
watchlist
]
}
op: add
}
Conclusion
In this article we have introduced the Sametime Proxy Server and its model for interacting with client applications. We showed how a Java application can be written to log in, receive asynchronous updates, and log out from the Proxy Server. Finally, we demonstrated how this code could be leveraged to perform Sametime actions, such as changing the user status, and receive notifications for other Sametime users.
Resources
About the author

Olivier Bernin is a Software Architect based at IBM's Dublin, Ireland, Software Lab. He is currently in charge of the Microsoft Office Integration and Proxy Server components of the IBM Sametime product. Prior to this assignment, Olivier was involved in the development of Sametime Advanced. You can contact him at
obernin@ie.ibm.com.